/*
 * This file is part of ELCube.
 *
 * ELCube is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ELCube is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ELCube.  If not, see <https://www.gnu.org/licenses/>.
 */
package cn.nkpro.elcube.components.financial.services

import cn.nkpro.elcube.co.spel.NkSpELManager
import cn.nkpro.elcube.financial.NkAbstractFinanceMethod
import cn.nkpro.elcube.financial.NkFinanceCustomRule
import cn.nkpro.elcube.financial.NkFinanceMethodOption
import cn.nkpro.elcube.financial.NkFinancePaymentI
import cn.nkpro.elcube.financial.NkFinanceRate
import cn.nkpro.elcube.utils.DateTimeUtilz
import com.alibaba.fastjson.JSONObject
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component


/**
 * 等额本金计算方法
 */
@Component("NkFinanceMethod02")
class NkFinanceMethod02 extends NkAbstractFinanceMethod {

    @Autowired
    NkSpELManager spELManager

    @Override
    List<ProgramDef> programDefs() {
        return Arrays.asList(
                new ProgramDef("RATE",          "调整利率",     false,false,false,true,false),
        )
    }

    @Override
    Object apply(Object json) throws Exception {

        NkFinanceMethodOption option  = new JSONObject(json as Map<String, Object>).toJavaObject(NkFinanceMethodOption.class)

        if (option.validate()){

            NkFinanceRate rate = new NkFinanceRate()
            rate.activeDate = 0
            rate.rate = option.ip
            option.rates.add(rate)

            Calendar calendar = Calendar.getInstance()
            calendar.setTimeInMillis(option.dt*1000)
            calendar.add(Calendar.MONTH, -option.tp)

            double residual = option.pv

            List<NkFinancePaymentI> payments = new ArrayList<>()

            for(int i=0;i<option.np;i++){

                Long prevExpireDate = (Long) (calendar.getTimeInMillis() / 1000)

                NkFinancePaymentI item = new NkFinancePaymentI()
                item.setPeriod(i+1)
                if(option.tp == 1 && option.dayOfPay > 0 && i == 0){
                    item.setExpireDate(nextDate(calendar,option.step,calendar.get(Calendar.DAY_OF_MONTH)));
                }else{
                    item.setExpireDate(nextDate(calendar,option.step,option.dayOfPay))
                }
                item.setInterest(i==0&&option.tp==1?0:doubleValue(residual*option.ip))
                // 计算期初利息
                if(item.period == 1 && option.dateOfInterest > option.dt){
                    long dayCount = DateTimeUtilz.dateDiff(item.expireDate,prevExpireDate)
                    item.interest = doubleValue(calcInterest(option.rates,option.dateOfInterest,prevExpireDate,dayCount,residual)+item.interest)
                }
                item.setPrincipal(doubleValue(option.pv/option.np))
                item.setPay(doubleValue(item.getInterest()+item.getPrincipal()))
                item.setResidual(doubleValue(residual-item.getPrincipal()))
                item.setFee(0d)
                item.setRemark(null)

                payments.add(item)

                residual = item.getResidual()
            }

            // 开始处理自定义规则
            if(option.customPayAmountRule!=null){
                // 循环处理自定义规则，注意，规则必须按激活时间顺序执行
                for(NkFinanceCustomRule ruleLoop in option.customPayAmountRule.findAll { r->r.activeDate}){
                    switch (ruleLoop.program){
                        case 'RATE':
                            execRate     (payments, option, ruleLoop)
                            break
                    }
                }
            }

            if((option.fv==null || option.fv==0)&&!payments.isEmpty()){
                NkFinancePaymentI last = payments.get(payments.size()-1)
                last.setPrincipal(doubleValue(last.getPrincipal() + last.getResidual()))
                last.setPay(doubleValue(last.getPay() + last.getResidual()))
                last.setResidual(0d)
            }

            return payments
        }

        return null
    }

    private void execRate(List<NkFinancePaymentI> payments, NkFinanceMethodOption option, NkFinanceCustomRule ruleLoop){

        // 生成利率表
        NkFinanceRate rate = new NkFinanceRate()
        rate.activeDate = ruleLoop.activeDate
        rate.rate = (ruleLoop.rate?:0) / option.pf //期间利率 = 年利率 / 付款频率
        option.rates.add(rate)

        // 调息日后的（含）第一个还款日期
        NkFinancePaymentI next = payments.find{ payment -> payment.expireDate >= ruleLoop.activeDate }
        if(next){
            NkFinancePaymentI prev

            // 如果第一个还款日不是调息日，说明该期次需要分段计算利息
            if(next.expireDate != ruleLoop.activeDate){
                // 获取前一期的到期日期
                def pres = payments.findAll { payment -> payment.expireDate < ruleLoop.activeDate }
                prev = pres.isEmpty()?null:pres.last()

                next.interest = calcInterest(option.rates,prev?prev.expireDate:option.dt,next.expireDate,prev?prev.residual:option.pv)
                next.pay = doubleValue(next.interest+next.principal)
            }

            // 重新计算利息
            prev = next
            payments
                .findAll{ payment -> payment.expireDate > prev.expireDate }
                .forEach{ payment ->
                    payment.interest = calcInterest(option.rates,prev.expireDate,payment.expireDate,prev.residual)
                    payment.pay = doubleValue(payment.interest+payment.principal)
                    prev = payment
                }

        }
    }
}
